/**
 * @projectDescription
 * construct javascript framework :: building a better web
 * 
 * Much of this framework has been adapted from or inspired by the excellent: 
 *   Prototype <http://www.prototypejs.org/>, 
 *   jQuery    <http://jquery.com/>, and
 *   Mootools  <http://mootools.net/> 
 * frameworks (which are all available under the MIT license). The creators 
 * and contributors of these frameworks rock and without them, construct 
 * (and the web as we know it) wouldn't exist. Comments, feeback, bugs and 
 * donations (all of which are greatly appreciated) should be directed to the 
 * construct website: <http://constructjs.org/>.
 * 
 * @version 0.1.0.0
 * @author Brent Broome
 * @copyright 2008, Brent Broome
 * @license MIT <http://www.opensource.org/licenses/mit-license.php>
 */

// $: Element, ElementList, Event, Cache, Hash, Cookie, Ajax, Fx, Template

 
/**
 * This is the core construct object, which happens to act a lot like the $
 * function from Prototype. If you pass it a String, it returns the Element 
 * in the document with the matching id (or null if no such Element exists). 
 * If you pass it a native DOM node, it returns it wrapped in an Element. 
 * If you pass it an Element, it returns it unchanged. For selecting multiple
 * elements by id or using selectors, see $.select
 * 
 * @see Element
 * @param {String,Node,Element} element The element id, native DOM node or 
 *  existing Element to wrap in an Element and return. 
 * @return {Element} The wrapped Element
 * @see $#select
 * @since 0.1
 */
function $(element) {
  if (typeof element == 'string')
    element = document.getElementById(element);    
    
  return $.Element.extend(element);
};

/*
 * Wrap everything in a monolithic anonymous function to allow the
 * definition of variables during the initialization of the framework
 * (for simplicity, efficiency and mungeability) without poluting the
 * global namespace.
 */
(function() {
 
/**
 * The current version of the framework, formatted as:
 * 'major,minor,revision,build', which are incremented as follows:
 * 
 *   major   : When there are major changes made to the architecture, 
 *             stability or scope of the framework
 *   minor   : When there is a change made to the API of the framework,
 *             such as adding or removing classes or methods, changing
 *             arguments, deprecating existing methods, etc. 
 *   revision: When a new version of the framework is released without
 *             any changes to the API (i.e. bug fix releases).
 *   build   : When ANY change is made to the framework (this is only 
 *             used internally for development and will always be 0
 *             for public releases)
 *             
 * Note: Incrementing any of the values zeros-out the values to its right.
 * @since 0.1
 */
$.Version = '0.1.0.0';

/**
 * An empty function, provided for convenience and clarity
 * @since 0.1
 */
$.EmptyFn = function() {};

/**
 * The identity function, which returns its argument unchanged
 * @param  {Object} x The object to return
 * @return {Object} The object passed as the argument
 * @since 0.1
 */
$.IdFn = function(x) { return x; };
  
    
// Some temporary variables for calculating the flags below
var userAgent = navigator.userAgent.toLowerCase(),
    mSafari   = /mobile.*safari/i.test(userAgent),
    webKitNdx = userAgent.indexOf('webkit/'),
    geckoNdx  = userAgent.indexOf('gecko/'),
    extraPP   = webKitNdx >= 0 || userAgent.indexOf('khtml/') >= 0,
    divProto  = document.createElement('div')['__proto__'],
    htmlEl    = !!window.HTMLElement;

// If our browser lacks the HTMLElement, see if we can add it 
if(divProto && !htmlEl) 
{
  window.HTMLElement = {};
  HTMLElement.prototype = divProto;
  htmlEl = true;
}

/**
 * A collection of flags related to browser capabilities and identity
 */
$.Browser = {  
  /**
   * Flags whether the browser skips iterating over built-in
   * functions such as toString and valueOf
   * @alias $.Browser.BuiltInsHidden
   * @since 0.1
   */
  BuiltInsHidden: !('toString' in { toString: 1 }),
  
  /**
   * Flags whether the browser has built-in Element extensions
   * @alias $.Browser.ElementExtensions
   * @since 0.1
   */
  ElementExtensions: !mSafari && divProto && 
    divProto !== document.createElement('form')['__proto__'],

  /**
   * Flags whether the browser has a quirk with post parameters
   * @alias $.Browser.ExtraPostParam
   * @since 0.1
   */
  ExtraPostParam: extraPP,
  
  /**
   * Flags whether the browser is a version of Gecko
   * @alias $.Browser.Gecko
   * @since 0.1
   */
  Gecko: geckoNdx >= 0 && !extraPP,

  /**
   * Flags whether the browser defines the HTMLElement class
   * @alias $.Browser.HTMLElement
   * @since 0.1
   */
  HTMLElement: !!window.HTMLElement,
    
  /**
   * Flags whether the browser is a version of Internet Explorer
   * @alias $.Browser.IE
   * @since 0.1
   */
  IE: window.attachEvent && !window.opera,

  /**
   * Flags whether the browser is a version of mobile safari
   * @alias $.Browser.MSafari
   * @since 0.1
   */
  MSafari: mSafari,

  /**
   * Flags whether the browser is a version of Gecko that has
   * a few particular quirks
   * @alias $.Browser.OldGecko
   * @since 0.1
   */
  OldGecko: (geckoNdx >= 0 ? parseInt(
    userAgent.slice(geckoNdx + 5, geckoNdx + 9)) : NaN) < 2005,

  /**
   * Flags whether the browser is a version of Opera
   * @alias $.Browser.Opera
   * @since 0.1
   */
  Opera: !!window.opera,

  /**
   * Flags whether the browser provides a built-in querySelector
   * @alias $.Browser.SelectorsAPI
   * @since 0.1
   */
  SelectorsAPI: !!document.querySelector,

  /**
   * Flags whether the browser is a version of WebKit
   * @alias $.Browser.WebKit
   * @since 0.1
   */
  WebKit: webKitNdx >= 0,

  /**
   * Flags whether the browser provides an XMLHttpRequest object
   * @alias $.Browser.XHR
   * @since 0.1
   */
  XHR: !!window.XMLHttpRequest,

  /**
   * Flags whether the browser provides XPath support
   * @alias $.Browser.XPath
   * @since 0.1
   */
  XPath: !!document.evaluate
};

/**
 * Adds all of the properties in source to destination (overwriting
 * them if they already exist) and returns destination.
 * @param {Object} destination The object to extend.
 * @param {Object} source      The object from which to extend.
 * @return {Object} destination, extended with all the properties in source
 * @since 0.1
 */
$.extend = function(destination, source) {
  for (var property in source) 
    destination[property] = source[property];   
  return destination;
};

/**
 * Returns a shallow clone of the passed object. Note that the type of
 * the passed object will be preserved, as the cloned object will be
 * constructed using the passed object's constructor.
 * @param {Object} object The object to clone
 * @return {Object} A clone of the object
 * @since 0.1
 */
$.clone = function(object) {
  return $.extend(new object.constructor, object);  
};

/**
 * Builds an Array from any object.  If the object has an asArray method, the
 * result of invoking that is returned.  Otherwise, if the object is iterable 
 * (has a length property and an indexer, such as NodeList or arguments), it
 * is iterated over an loaded into an Array. If object is null or undefined, an
 * empty Array is returned and in all other cases an Array containing the object
 * as its only element is returned.
 * @param {Object} object The object to build an Array from
 * @return {Array} The Array representation of the passed object
 * @since 0.1
 */
$.asArray = function(object) {
  if (object == null) return [];
  if (object.asArray) return object.asArray();
  
  var length = object.length, results = [];   	
  if (isNaN(length)) return [object]; 
  while (length--) 
    results[length] = object[length];      	
  return results;
};

/**
 * Adds methods to destination's prototype from source.
 * @param {Object} destination The object whose prototype should be extended
 * @param {Object} source      The object with the methods to add
 * @param {Object} overwrite   Flags whether to overwrite existing methods in
 *  destination's prototype with those in source
 * @since 0.1
 */
$.addMethods = function(destination, source, overwrite) {
  for (var property in source) 
    if ((!(property in destination.prototype) || overwrite) && 
        typeof source[property] == 'function') 
      destination.prototype[property] = source[property];   
      
  return destination;
};

/**
 * Tests whether the passed object is an instance (or sublcass) of
 * the class provided. Some examples:
 *   isA(x, Object) returns true if x isn't null, undefined or a primitive.
 *   isA(x, Number) returns true if x is a Number or a primitive number.
 *   isA(x, Car)    returns true if x is a Car or any subclass of Car.
 * @param  {Object}  object The object to test
 * @param  {Object}  object The class to check against
 * @return {Boolean} True if the object is an instance (or sublcass) of
 *  the class provided, false otherwise.
 * @since 0.1
 */
$.isA = function(object, type) {
  if(object == null || type == null) return false;  
  var ctor = object.constructor;
  
  do {
    if(ctor === type) return true;
  } while(ctor = ctor.superclass);
  
  return false;
};

/*-------------------------- Basic Element stuff ----------------------------*/
// up here so the $ function works even if the Element module is excluded

/**
 * This object wraps native DOM nodes (those things returned by native
 * functions such as document.getElementById) and provides simple,
 * efficient, cross-browser methods for querying and manipulating them. 
 * @since 0.1
 */
$.Element        = window.Element || {};
$.Element.extend = $.IdFn; 

/*-------------------------- Class Creation -------------------------------*/

/**
 * Creates a new class from the definition provided.
 * @param {Object} definition The class definition, which should be an 
 *  object containing the methods for this class as properties, as well as
 *  a reference to the superclass in a property named superclass (should
 *  inheritence be desired). If a superclass is specified and any of the 
 *  methods in the definition already exist in the superclass, they will be 
 *  overwritten and the original method in the superclass can be referenced
 *  by including an argument named $super as the first argument in the new
 *  method. The method that should be invoked when constructing
 *  new objects of this class should be provided in a property named
 *  construct, otherwise a default (which does nothing) will be used.
 *  Finally, $.isA will return true if an object of this class is tested 
 *  against a reference to one of its ancestor classes (including Object).
 * @return {Class} The newly created class
 * @since 0.1
 */
$.newClass = function(definition) {
  var superclass = definition.superclass,
      theClass   = function() { this.construct.apply(this, arguments); };      
  
  /**
   * The superclass of this class (or Object if none was provided)
   * @alias Class.superclass
   * @since 0.1
   */
  theClass.superclass  = superclass || Object;
  
  /**
   * Adds methods to this class that exhibit the expected behavior if they
   * take a first argument named $super. 
   * @param {Object} An object containing the methods to add to this class
   * @return {Class} This class, now containing the new methods
   * @alias Class.addMethods
   * @since 0.1
   */
  theClass.addMethods = function(methods) {
    var superclass = this.superclass && this.superclass.prototype,
        property, value, method;

    if ($.Browser.BuiltInsHidden)
      properties.push("toString", "valueOf");

    for (property in methods) {
      value = methods[property];
      
      if (superclass && typeof value == 'function' &&
          value.argumentNames()[0] == "$super") {
        method = value;
        value  = (function(m) {
          return function() { 
            return superclass[m].apply(this, arguments) 
          };
        })(property).wrap(method);
        
        value.valueOf  = method.valueOf.bindTo(method);
        value.toString = method.toString.bindTo(method);
      }
      
      this.prototype[property] = value;
    }

    return this;
  };
  
  theClass.prototype = superclass ? $.clone(superclass.prototype) : {};
  theClass.addMethods(definition); 
  theClass.prototype.constructor = theClass;
      
  /**
   * Function called from the constructor of this class whenever
   * an object of this class is created.
   * @param {Object} [args] Zero or more arguments to pass to the
   *  construct method of your class, as appropriate.
   * @return The newly-constructed instance of this class
   * @alias Class.prototype.construct
   * @since 0.1
   */
  if(!definition.construct) 
    theClass.prototype.construct = $.EmptyFn;        
  
  return theClass;
};

/*-------------------------- Function extensions ----------------------------*/

$.addMethods(Function, {  
  /**
   * Gets the argument names from this function's definition
   * @return {Array} An array containing this function's argument names
   * @alias Function.prototype.argNames
   * @since 0.1
   */
  argNames: function() {
    var names = this.toString().match($._RExEx.AN)[1]
      .replace($._RExEx.WP, '').split(',');
      
    return names.length == 1 && !names[0] ? [] : names;
  },

  /**
   * Returns a version of this function that will execute in the context
   * of the passed object (that is, the keyword this will refer to the 
   * passed object when the returned function is executed).
   * @param  {Object}   object The object to execute in the context of
   * @return {Function} A version of this function that will be invoked
   *  in the context of the passed object.
   * @alias Function.prototype.bindTo
   * @since 0.1
   */
  bindTo: function(object) {
    var self = this;
    
    return function() {
      return self.apply(object, arguments);
    }
  },
  
  /**
   * Transforms this function from one that takes an Object of type T
   * as its first argument to one that is a method of type T, which 
   * invokes the original method with itself as the first argument.
   * @return The methodized function
   * @alias Function.prototype.methodize
   * @since 0.1
   */
  methodize: function() {
    var self = this;
    
    return function() {
      return self.apply(null, [this].concat($.A(arguments)));
    };
  },
  
  /**
   * Sets this function up for execution after the delay specified (in ms)
   * @param {Number} delay The number of ms in the future to schedule 
   *  execution for.
   * @param {Object} [args] Zero or more arguments to pass to the delayed 
   *  function
   * @return The timer, which can be passed to $.cancel before
   *  the delay has elapsed to cancel the scheduled execution.
   * @alias Function.prototype.delay
   * @since 0.1
   */
  delay: function() { 
    var self = this, 
        args    = $.A(arguments), 
        timeout = args.shift();
         
    return setTimeout(function() { return self.apply(self, args); }, timeout);
  },
  
  /**
   * Defers the execution of this function for 10 ms. This is the same as
   * calling delay with 10 as the first argument and is simply provided
   * as a convenience.
   * @param {Object} [args] Zero or more arguments to pass to the deferred 
   *  function
   * @return The timer, which can be passed to $.cancel within 10 ms to 
   *  cancel the scheduled execution.
   * @alias Function.prototype.defer
   * @since 0.1
   */
  defer: function() {
    // We don't just use delay here to avoid the extra method invocation,
    // array creation and array shift operations
    var self = this;  
    return setTimeout(function() { return self.apply(self, arguments); }, 10);
  },
  
  /**
   * Wraps this function with the passed function
   * @param {Function} wrapper 
   */
  wrap: function(wrapper) {
    var self = this;    
    return function() {
      return wrapper.apply(this, [self.bind(this)].concat($A(arguments)));
    }
  }
});

/*--------------------------- Date extensions -------------------------------*/

/**
 * When invoked, returns the current timestamp (just like new Date().getTime())
 * @return The current timestamp (ms since epoch)
 * @since 0.1
 */
Date.now = Date.now || function() {
  return new Date().getTime();
};

/*-------------------------- RegExp extensions ------------------------------*/

/**
 * Returns a copy of the passed String with all characters that have
 * special meaning in a regular expression escaped.
 * @param {String} string The string to escape
 * @return {String} The escaped string
 * @since 0.1
 */
RegExp.escape = function(string) {
  return String(string).replace($._REx.SR, '\\$1');
};

/*
 * Private object for storing precompiled regular expressions
 */
$._RExEx = {
  A : /&/g,                                     // ampersand  
  AE: /&amp;/g,                                 // encoded ampersand
  AL: /alpha\([^\)]*\)/ig,                      // alpha
  AN: /^[\s\(]*function[^(]*\(([^\)]*)\)/,      // function arg names
  AO: /alpha\(opacity=(.*)\)/,                  // alpha(opacity)  
  AS: /<script[^>]*>([^\\x00]*?)<\/script>/ig,  // all scripts
  B : /^\s*$/,                                  // blank string
  C : /([A-Z]+)([A-Z][a-z])/,                   // camel token
  D : /-/,                                      // dash
  DC: /::/,                                     // double colon
  DO: /^(\d+)$/,                                // digit only
  DQ: /"/g,                                     // double-quote
  EC: /([a-z\d])([A-Z])/,                       // end of camel token
  F : /^[\s\(]*function[^(]*\(([^\)]*)\)/,      // function names
  GT: />/g,                                     // greater-than
  GE: /&gt;/g,                                  // encoded greater-than
  JC: /^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i, // js content-type
  JS: /^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/,   // JSON test
  LT: /</g,                                     // less-than
  LE: /&lt;/g,                                  // encoded less-than
  NW: /\S/,                                     // no whitespace
  NX: /(\[[\w-]*?:|:checked)/,                  // no XPath
  O : /^\s*https?:\/\/[^\/]*/,                  // origin
  OP: /opacity:\s*(\d?\.?\d*)/,                 // opacity
  OS: /<script[^>]*>([^\\x00]*?)<\/script>/i,   // one script
  P : /\\./g,                                   // period
  QP: /([^?#]*)(#.*)?$/,                        // query parameters
  SQ: /'/g,                                     // single-quote
  SR: /([.*+?^=!:${}()|[\]\/\\])/g,             // special RegExp characters
  SS: /(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, // $.Selector split
  ST: /"[^"\\\n\r]*"/g,                         // special text characters
  SW: /\s/,                                     // single whitespace
  T : /<\/?[^>]+>/gi,                           // tags
  TP: /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, // inner template pattern
  U : /_/,                                      // underscore
  UC: /[\x00-\x1f\\]/,                          // Unicode characters
  WB: /\s*$/,                                   // whitespace on back
  WF: /^\s*/,                                   // whitespace on front
  WP: /\s+/,                                    // whitespace+
  XF: /^(-?\d*)?n(([+-])(\d+))?/                // XPath function ab + b
};

/*------------------------- Some utility methods ----------------------------*/

/**
 * Casts the value to a string safely (no Error even if the value is
 * undefined) and also returns '' for null values instead of 'null'
 * @param {Object} value The value to cast to a string
 * @return {String} The string representation of the value
 * @since 0.1
 */
$.asString = function(value) {
  return value == null ? '' : String(value);
};

/**
 * Cancels the specified timer (whether an interval or timeout),
 * such that it will never fire again (or ever, if it has yet to).
 * @param {Object} timer The timer to cancel
 * @since 0.1
 */
$.cancel = function(timer) {    
	clearTimeout(timer);
	clearInterval(timer);
};

/**
 * Gets a string representation of an object that aims to be more detailed
 * and/or informative than toString 
 * @param {Object} object The object to inspect
 * @since 0.1
 */
$.inspect = function(object) {
  return object == null ? object === null ? 'null' : 'undefined' : 
    object.inspect ? object.inspect() : String(object);
};

/**
 * Tests whether the passed object is an Array
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is an Array, false otherwise
 * @since 0.1
 */
$.isArray = function(object) {
  return object && object.constructor === Array;
};

/**
 * Tests whether the passed object is defined (not null or undefined)
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is defined, false otherwise
 * @since 0.1
 */
$.isDefined = function(object) {
  return object != null;
};

/**
 * Tests whether the passed object is an Function
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is an Function, false otherwise
 * @since 0.1
 */
$.isFunction = function(object) {
  return typeof object == 'function';
};

/**
 * Tests whether the passed object is an DOM node (included those 
 * wrapped in Elements)
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is a DOM node or an Element, 
 *  false otherwise
 * @since 0.1
 */
$.isNode = function(object) {
  return object != null && object.nodeType == 1;
};

/**
 * Tests whether the passed object is an Number
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is an Number, false otherwise
 * @since 0.1
 */
$.isNumber = function(object) {
  return typeof object == 'number';
};

/**
 * Tests whether the passed object is an Object (and not null)
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is an Object and not 
 *  null, false otherwise
 * @since 0.1
 */
$.isObject = function(object) {
  return typeof object == 'object' && object != null;
};

/**
 * Tests whether the passed object is an String
 * @param  {Object}  object The object to test
 * @return {Boolean} True if the object is an String, false otherwise
 * @since 0.1
 */
$.isString = function(object) {
  return typeof object == 'string';
};

/**
 * Gets all of the associative keys for an object
 * @param {Object} object The object to retrieve the keys for
 * @return {Array} An array containing all the keys of the object
 * @since 0.1
 */
$.keys = function(object) {
  var property, keys = [];  
  for (property in object) 
    keys.push(property);    
  return keys;
};

/**
 * Calls the object's toHTML function (if there is one) or converts it to a
 * String if there isn't one
 * @param {Object} object The object to convert to HTML
 * @return {String} The HTML representation of the object
 * @since 0.1
 */
$.toHTML = function(object) {
  return object == null ? '' : object.toHTML ? object.toHTML() : String(object);
};

/**
 * Gets all of the associative values for an object
 * @param {Object} object The object to retrieve the values for
 * @return {Array} An array containing all the values of the object
 * @since 0.1
 */
$.values = function(object) {
  var values = [];
  for (var property in object)
    values.push(object[property]);
  return values;
};
  
/*------------------------- Native String extensions ------------------------*/

$.extend(String.prototype, {
  /**
   * Tokenizes this string using the passed string or regular expression
   * if provided, or using whitespace as a delimiter otherwise.  Note that
   * whitespace is stripped from the ends of the string before it is split,
   * so you don't end up with whitespace tokens at the beginning or end.
   * @param {Object} [seperator] The string or regular expression that should
   * be used to split this string
   * @return {Array} An array of String tokens
   * @alias String.prototype.tokenize
   * @since 0.1
   */
  tokenize: function(seperator) {
    var string = this.strip();
    return string ? string.split(seperator || $._REx.WP) : [];
  },

  /**
   * Strips all whitespace from the beginning and end of this string
   * @return {String} This string with whitespace stripped from its ends
   * @alias String.prototype.strip
   * @since 0.1
   */
  strip: function() {
    return this.replace($._REx.WF, '').replace($._REx.WB, '');
  },

  /**
   * Strips all XML/HTML tags from this string
   * @return {String} This string with all tags stripped
   * @alias String.prototype.stripTags
   * @since 0.1
   */
  stripTags: function() {
    return this.replace($._REx.T, '');
  },

  /**
   * Strips all script tags and their contents from this string
   * @return {String} This string with all scripts stripped
   * @alias String.prototype.stripScripts
   * @since 0.1
   */
  stripScripts: function() {
    return this.replace($._REx.AS, '');
  },

  /**
   * Extracts the contents of all the script tags from this string and
   * returns them in an Array
   * @return {Array} An array containing the contents of each script tag
   * @alias String.prototype.extractScripts
   * @since 0.1
   */
  extractScripts: function() {
    return (this.match($._REx.AS) || []).map(function(scriptTag) {
      return (scriptTag.match($._REx.OS) || ['', ''])[1];
    });
  },
  
  /**
   * Evaluates the scripts contained in this string
   * @alias String.prototype.evalScripts
   * @since 0.1
   */
  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  /**
   * Escapes all occurances of &, < and > with their HTML entities
   * @return this string with all ampersands, and angle brackets replaced
   *  with the corresponding HTML entity strings
   * @alias String.prototype.escapeHTML
   * @since 0.1
   */
  escapeHTML: function() {
    return this.replace($._REx.Amp,'&amp;').replace(
      $._REx.LT,'&lt;').replace($._REx.GT,'&gt;');
  },

  /**
   * Unescapes the basic HTML entities (&amp;, &lt; and &gt;)
   * @return this string with the basic HTML entities converted to the
   * corresponding characters
   * @alias String.prototype.unescapeHTML
   * @since 0.1
   */
  unescapeHTML: function() {
    return this.replace($._REx.AmpE,'&').replace(
      $._REx._L,'<').replace($._REx._G,'>');
  },

  /**
   * Explodes this String into a character array
   * @return {Array} An Array containing this string's characters
   * @alias String.prototype.asArray
   * @since 0.1
   */
  asArray: function() {
    return this.split('');
  },

  /**
   * Returns a new string containing this string repeated count times
   * @param {Object} count The number of times to repeat this string
   * @return {String} A string containing this string repeated count times
   * @alias String.prototype.times
   * @since 0.1
   */
  times: function(count) {
    var result = '';
    while(count-- > 0) result += this;
    return result;
  },

  /**
   * Camel-cases a string that is currently dasherized
   * @return {String} This string with dashes replaced by camel case
   * @alias String.prototype.camelize
   * @since 0.1
   */
  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  /**
   * Returns a copy of this string with the first letter capitalized
   * @return {String} A copy of this string with the first letter capitalized
   */
  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },
  
  /**
   * Dasherizes a string that is currently camel-cased
   * @return {String} A copy of this string with all of the camelized breaks
   * replaced with dashes and all characters lower-case
   */
  dasherize: function() {
    return this.replace(/([A-Z])/, "-$1").toLowerCase(); 
  },

  
  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub($._REx.UC, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    return useDoubleQuotes ? ('"' + escapedString.replace($._REx.DQ, '\\"') + '"') :
           ("'" + escapedString.replace($._REx.SQ, '\\\'') + "'");
  },

  toJSON: function() {
    return this.inspect(true);
  },
  
  isJSON: function() {
    return ($._REx.JS).test(
      this.replace($._REx.P, '@').replace($._REx.SC, ''));
  },

  evalJSON: function(sanitize) {
    try {
      if (!sanitize || this.isJSON()) return eval('(' + this + ')');
    } catch (e) {}
    throw new SyntaxError('Not JSON: ' + this.inspect());
  },

  contains: function(pattern) {
    return this.indexOf(pattern) >= 0;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  isEmpty: function() {
    return this == '';
  },

  isBlank: function() {
    return $._REx.B.test(this);
  },

  interpolate: function(object, pattern) {
    return new $.Template(this, pattern).evaluate(object);
  }
});

/**<number.js>**/

/**<enumerable.js>**/

/**<array.js>**/

/**<hash.js>**/

/**
 * Formats the passed object as a query string
 * @param {Object} object The object to convert to a query string
 * @param {Array} [excluded] An array of property names to be excluded
 *  from the query string
 * @since 0.1
 */
$.toQueryString = function(object, excluded) {
  return $.Hash(object).toQueryString(excluded);
};

/**
 * Parses a query string and returns the paramters as a Hash
 * @param {Object} [separator] A String or RegExp representing the parameter
 * seperator to use (& is used if this is ommitted)
 * @return {$.Hash} A Hash containing the parameter names (as keys) and values
 */
String.prototype.toQueryParams = function(separator) {
  var match = this.strip().match($._REx.QP);
  if (!match) return {};

  return match[1].split(separator || '&').inject({}, function(hash, pair) {
    if ((pair = pair.split('='))[0]) {
      var key = decodeURIComponent(pair.shift());
      var value = pair.length > 1 ? pair.join('=') : pair[0];
      if (value != undefined) value = decodeURIComponent(value);

      if (key in hash) {
        if (!$.isArray(hash[key])) hash[key] = [hash[key]];
        hash[key].push(value);
      }
      else hash[key] = value;
    }
    return hash;
  });
};

/**<range.js>**/

/**<browsers.js>**/

/**<elementCore.js>**/

/**<elementStyles.js>**/

/**<elementUtils.js>**/

/**<selector.js>**/

/**<form.js>**/

/**<event.js>**/

/**<ajax.js>**/

/**<fx.js>**/

})(construct); // end global wrapper
